package cn.ante.ymcc.service.impl;

import cn.ante.ymcc.domain.PayFlow;
import cn.ante.ymcc.domain.PayOrder;
import cn.ante.ymcc.dto.AlipayNotifyDto;
import cn.ante.ymcc.dto.PayAliNotifyDto;
import cn.ante.ymcc.exception.GlobalException;
import cn.ante.ymcc.service.IPayFlowService;
import cn.ante.ymcc.service.IPayNotifyService;
import cn.ante.ymcc.service.IPayOrderService;
import cn.ante.ymcc.util.AssertUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.alipay.easysdk.factory.Factory;

import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

@Service
@Slf4j
@Transactional
public class PayNotifyServiceImpl implements IPayNotifyService {
    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    @Autowired
    private IPayOrderService payOrderService;

    @Autowired
    private IPayFlowService  payFlowService;
    @Override

    public String getNotify(AlipayNotifyDto dto) {
        log.info("接收支付宝异步通知");
        // 1.判断支付宝回调结果的状态是不是成功，不是成功就报错，等待重试
        AssertUtil.isTrue(dto.isTradeSuccess(),"交易未成功");

        // 2.验证签名 先将回调结果解析
        Map map = JSONObject.parseObject(JSON.toJSONString(dto), Map.class);

        try {
            Boolean verifyNotify = Factory.Payment.Common().verifyNotify(map);
            if (!verifyNotify){
                return "success";  // 验签不通过  说明有问题 则告诉不要子啊回调
            }
        } catch (Exception e) {
            e.printStackTrace();
            return "error"; // 再次通知  重试
        }

        // 3.判断回调结果的中的业务是否正确
        String orderNo = dto.getOut_trade_no();
        PayOrder payOrder = payOrderService.checkPayOrder(orderNo);
        // 3,1判断该订单号是否存在
        if (payOrder == null){
            return "success"; // 说明订单不存在 不要在重试通知
        }
        // 3.2判断金额是否与回调中的一致
        int amountEq = new BigDecimal(dto.getTotal_amount()).compareTo(payOrder.getAmount());
        if (amountEq != 0){
            return "success"; // 金额不对，不要再重试
        }

        // 判断订单的状态是否是未支付
        boolean isWillPay = payOrder.getPayStatus() == PayOrder.STATE_WILL_PAY;
        if (!isWillPay){
            try {
                // 当支付订单不是未支付，说明已经支付成功，那就走退款逻辑
                Factory.Payment.Common().refund(dto.getOut_trade_no(),dto.getTotal_amount());
            } catch (Exception e) {
                e.printStackTrace();
                // 退款逻辑中出现异常，就走人工执行兜底方法
            }
            return "success"; // 订单状态不对，不要再重试
        }

        // 4 上述判断都没有问题，就修改订单中的字段值
        payOrder.setPayStatus(PayOrder.STATE_SUCCESS);
        Date now = new Date();
        payOrder.setUpdateTime(now);
//        payOrderService.update(payOrder); 交给事务监听器做 发送事务消息到mq 保证支付宝回调支付成功，订单服务和课程服务的状态都改变

        // 5.成功就需要记录交易流水
        PayFlow payFlow = new PayFlow();
        payFlow.setCode(dto.getCode());
        payFlow.setNotifyTime(now);
        payFlow.setPaySuccess(dto.isTradeSuccess());
        payFlow.setSubject(dto.getSubject());
        payFlow.setMsg(dto.getMsg());
        payFlow.setOutTradeNo(dto.getOut_trade_no());
        payFlow.setPassbackParams(dto.getPassback_params());
        payFlow.setTotalAmount(new BigDecimal(dto.getTotal_amount()));
        payFlow.setTradeStatus(dto.getTrade_status());
//        payFlowService.insert(payFlow); 交给事务监听器做

        // 6 处理支付结果的事务消息
        // 包装执行本地事务的参数
        Map<String, Object> transMap = new HashMap<>();
        transMap.put("payorder",payOrder);
        transMap.put("payflow",payFlow);
        // 包装发送事务消息的参数
        // 事务消息中包含订单号、登陆人、课程id
        PayAliNotifyDto payAliNotifyDto = new PayAliNotifyDto(
          payOrder.getOrderNo(),
          payOrder.getExtParams()
        );
        Message<PayAliNotifyDto> message = MessageBuilder.withPayload(payAliNotifyDto).build();

//        Message<String> message = MessageBuilder.withPayload(JSON.toJSONString(order2PayDto)).build();
        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxPayAliNotifyGroup",
                "paynofity-topic:aliay-tags",
                message, // 最终消费者需要的消息内容 即需要传递的参数
                transMap); // 执行本地事务需要的参数
        

        // 执行本地事务的状态
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();

        // 发事务送消息的状态
        SendStatus sendStatus = sendResult.getSendStatus();
        if( localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK){
            throw new GlobalException("处理支付结果失败！请重给我推消息","401");
        }



        return "success";
    }

    /**
    * @Description: 执行本地事务方法
    * @Author: Nai
    * @Version: 1.0
    * @Date:  2022/8/22 13:53
    */
    @Override
    public void updatePayAndSaveFlow(Map<String, Object> map) {
        PayOrder payOrder = (PayOrder)map.get("payorder");
        PayFlow payflow =(PayFlow)map.get("payflow");
        // 根据id查到该支付单
        PayOrder payOrderTmp = payOrderService.selectById(payOrder);
        // 为什么这里还要判断未支付  上面已经判断过了
        boolean isWillPay = payOrderTmp.getPayStatus() == PayOrder.STATE_WILL_PAY;
        AssertUtil.isTrue(isWillPay,"非法请求");
        payOrderService.updateById(payOrder);
        payFlowService.insert(payflow);
    }
}
